package com.jdbcpool;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Statement;

public class ConnectionProxy implements InvocationHandler {
	private Connection targetConnection;
	private Connection proxyConnection;
	private JdbcPoolDataSource ds;

	ConnectionProxy(Connection target, JdbcPoolDataSource ds) {
		setTargetConnection(target);
		setDs(ds);
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		try {
			if (method.getName().equals("close")) {
				if (!targetConnection.getAutoCommit()) {
					targetConnection.rollback();
				}
				releaseConnection();
				return null;
			} else if (method.getName().equals("createStatement")) {
				Statement stmt = (Statement) method.invoke(targetConnection, args);
				Statement mystmt = (Statement) Proxy.newProxyInstance(stmt
						.getClass().getClassLoader(),
						new Class[] { Statement.class }, new StatementProxy(
								stmt, this.ds));
				return mystmt;
			} else if (method.getName().equals("prepareStatement")) {
				PreparedStatement pstmt = (PreparedStatement) method.invoke(
						targetConnection, args);
				PreparedStatementProxy pstmtproxy = new PreparedStatementProxy(
						pstmt, this.ds);
				pstmtproxy.setSql((String) args[0]);
				Statement mystmt = (PreparedStatement) Proxy.newProxyInstance(
						pstmt.getClass().getClassLoader(),
						new Class[] { PreparedStatement.class }, pstmtproxy);
				return mystmt;
			} else if (method.getName().equals("prepareCall")) {
				CallableStatement cstmt = (CallableStatement) method.invoke(
						targetConnection, args);
				CallableStatementProxy pstmtproxy = new CallableStatementProxy(
						cstmt, this.ds);
				pstmtproxy.setSql((String) args[0]);
				Statement mystmt = (CallableStatement) Proxy.newProxyInstance(
						cstmt.getClass().getClassLoader(),
						new Class[] { CallableStatement.class }, pstmtproxy);
				return mystmt;
			} else {
				return method.invoke(targetConnection, args);
			}
		} catch (Exception e) {
			if ( e.getCause() ==null) {
				throw e;
			}else{
				throw e.getCause();
			}
		}
	}

	public JdbcPoolDataSource getDs() {
		return ds;
	}

	public void setDs(JdbcPoolDataSource ds) {
		this.ds = ds;
	}
	
	public void releaseConnection(){
		ds.getActivePool().remove(this);
		ds.getIdlePool().add(this);	
	}

	public Connection getTargetConnection() {
		return targetConnection;
	}

	public void setTargetConnection(Connection targetConnection) {
		this.targetConnection = targetConnection;
	}

	public Connection getProxyConnection() {
		return proxyConnection;
	}

	public void setProxyConnection(Connection proxyConnection) {
		this.proxyConnection = proxyConnection;
	}

}
